The plan:

  1. Loading all data.
  2. Extracting statistics regarding PIDs (cases) and syscall types (actions).
  3. Computation of basic aggregations and visualization of the results.

The original source system audit logs are the property of KnowledgePit.ai platform and can be downloaded from: https://knowledgepit.ai/fedcsis-2023-challenge/

The available data contains 20044 log files. Each of the files corresponds to 10 minutes of syscall history. In total, 698 log files (3.48%) correspond to attacks on the devices.

To enable the analysis of this data using process mining techniques, I would suggest to consider the combination of a time window ID ( time_window_id ) and SYSCALL_pid values as cases and combinations of SYSCALL_syscall, SYSCALL_success, and maybe PROCESS_uid values as actions.

I transform the data by selecting SYSCALL_timestamp, SYSCALL_pid, SYSCALL_success, PROCESS_uid, and SYSCALL_syscall columns. I also merge all data tables into a single table for more convenient processing. Finally, I divide the data into three parts:

load(file = "process_data_v2.RData")

process_discovery_data[SYSCALL_success == "", SYSCALL_success := "NA"]
model_training_data[SYSCALL_success == "", SYSCALL_success := "NA"]
test_data[SYSCALL_success == "", SYSCALL_success := "NA"]

setkey(process_discovery_data, time_window_id, SYSCALL_timestamp)
setkey(model_training_data, time_window_id, SYSCALL_timestamp)
setkey(test_data, time_window_id, SYSCALL_timestamp)
eventlog_data <- eventlog(as.data.frame(process_discovery_data),
                          case_id = c("time_window_id", "SYSCALL_pid"),
                          timestamp = "SYSCALL_timestamp",
                          activity_id = c("PROCESS_uid", "SYSCALL_syscall", "SYSCALL_success"),
                          activity_instance_id = c("time_window_id", "SYSCALL_pid", "PROCESS_uid", 
                                                   "SYSCALL_syscall", "SYSCALL_success", "SYSCALL_timestamp"),
                          resource_id = c("PROCESS_uid"),
                          lifecycle_id = "is_attack",
                          order = "alphabetical"
                          )

eventlog_data
# Log of 9500500 events consisting of:
159721 cases 
2212620 instances of 77 activities 
9 resources 
Events occurred from 2023-09-01 until 2023-09-01 16:39:59 
 
# Variables were mapped as follows:
Case identifier:        time_window_id_SYSCALL_pid 
Activity identifier:        PROCESS_uid_SYSCALL_syscall_SYSCALL_success 
Resource identifier:        PROCESS_uid 
Activity instance identifier:   time_window_id_SYSCALL_pid_PROCESS_uid_SYSCALL_syscall_SYSCALL_success_SYSCALL_timestamp 
Timestamp:          SYSCALL_timestamp 
Lifecycle transition:       is_attack 
trace_data <- traces(eventlog_data)
trace_data

frequent_traces <- filter_trace_frequency(eventlog_data, percentage = .8)
frequent_traces
# Log of 1697630 events consisting of:
127939 cases 
851163 instances of 24 activities 
3 resources 
Events occurred from 2023-09-01 until 2023-09-01 16:39:59 
 
# Variables were mapped as follows:
Case identifier:        time_window_id_SYSCALL_pid 
Activity identifier:        PROCESS_uid_SYSCALL_syscall_SYSCALL_success 
Resource identifier:        PROCESS_uid 
Activity instance identifier:   time_window_id_SYSCALL_pid_PROCESS_uid_SYSCALL_syscall_SYSCALL_success_SYSCALL_timestamp 
Timestamp:          SYSCALL_timestamp 
Lifecycle transition:       is_attack 
attack_traces <- filter_lifecycle_presence(eventlog_data, lifecycles = "TRUE", method = "all")
attack_traces
# Log of 462743 events consisting of:
5733 cases 
89658 instances of 44 activities 
5 resources 
Events occurred from 2023-09-01 until 2023-09-01 16:39:59 
 
# Variables were mapped as follows:
Case identifier:        time_window_id_SYSCALL_pid 
Activity identifier:        PROCESS_uid_SYSCALL_syscall_SYSCALL_success 
Resource identifier:        PROCESS_uid 
Activity instance identifier:   time_window_id_SYSCALL_pid_PROCESS_uid_SYSCALL_syscall_SYSCALL_success_SYSCALL_timestamp 
Timestamp:          SYSCALL_timestamp 
Lifecycle transition:       is_attack 
flow_plot <- process_map(frequent_traces, type = frequency("relative"))
flow_plot

frequent_attack_traces <- filter_trace_frequency(attack_traces, percentage = .8)
attacks_flow_plot <- process_map(frequent_attack_traces, type = frequency("relative"))
attacks_flow_plot

variant_viz <- trace_explorer(eventlog_data, coverage = 0.5)
variant_viz

eventlog_data_dt <- as.data.table(eventlog_data)

unique_action_ids <- eventlog_data_dt[, unique(PROCESS_uid_SYSCALL_syscall_SYSCALL_success)]
trace_dt <- eventlog_data_dt[, 
                             .(trace = paste(PROCESS_uid_SYSCALL_syscall_SYSCALL_success, 
                                             collapse = ","),
                               trace_length = .N),
                             by = time_window_id_SYSCALL_pid]

dim(trace_dt)
[1] 159721      3
trace_dt[, uniqueN(trace)]
[1] 3351
trace_dt <- compact_traces(trace_dt, unique_action_ids, 
                           show_progress = FALSE)

# trace_dt[, compacted_trace := copy(trace)]
# 
# trace_dt[, compacted_trace := gsub("root_close_yes,root_openat_yes",
#                                    "root_close_openat_yes", 
#                                    compacted_trace)]
# trace_dt[, compacted_trace := gsub("(,root_close_openat_yes){2,}", 
#                                    ",multi_root_close_openat_yes",
#                                    compacted_trace)]
# trace_dt[, compacted_trace := gsub("^root_close_openat_yes,multi_root_close_openat_yes",
#                                    "multi_root_close_openat_yes",
#                                    compacted_trace)]
# trace_dt[, compacted_trace := gsub("^root_close_openat_yes,root_close_openat_yes",
#                                    "multi_root_close_openat_yes",
#                                    compacted_trace)]
# trace_dt[, uniqueN(compacted_trace)]
# 
# for(action in unique_action_ids) {
#   trace_dt[, compacted_trace := gsub(paste0("(,", action, "){2,}"), 
#                                      paste0(",multi_", action), 
#                                      compacted_trace)]
#   trace_dt[, compacted_trace := gsub(paste0("^", action, ",multi_", action), 
#                                      paste0("multi_", action), 
#                                      compacted_trace)]
#   trace_dt[, compacted_trace := gsub(paste0("^", action, ",", action), 
#                                      paste0("multi_", action), 
#                                      compacted_trace)]
#   print(action)
# }
# trace_dt[, uniqueN(compacted_trace)]
# trace_dt[, compacted_trace_length := sapply(strsplit(compacted_trace, ","), length)]
normalized_levenshtein_dist <- function(x, y) {
  ldist <- proxy::dist(list(x), list(y), method = "Levenshtein")
  as.numeric(ldist)/(length(x) + length(y))
}

dist_matrix <- proxy::dist(strsplit(trace_dt[, unique(compacted_trace)], ","), 
                           method = normalized_levenshtein_dist)

hclustering <- hclust(dist_matrix, method = "ward.D")

mds_embedds <- cmdscale(dist_matrix, k = 2)
plot(mds_embedds, col = cutree(hclustering, k = 5), 
     xlab = "latent dim 1", 
     ylab = "latent dim 2",
     main = "Multidimensional scaling of trace variants")

save(dist_matrix, mds_embedds, file = "variant_distance_matrix.RData")

fig1 <- ggplot(data.table(mds_embedds), 
               aes(x=V1, y=V2, colour = factor(cutree(hclustering, k = 5)))) +
  geom_point() + 
  labs(x = "latent dim 1", y = "latent dim 2", colour = "group", 
       title = "Multidimensional scaling of trace variants")
fig1

process_discovery_data[, time_window_id_SYSCALL_pid := paste0(time_window_id, "_", SYSCALL_pid)]
trace_dt <- trace_dt[process_discovery_data[, .(attack = is_attack[1]), 
                                            by = time_window_id_SYSCALL_pid], 
                     on = "time_window_id_SYSCALL_pid"]

global_mean <- trace_dt[, mean(attack)]
variant_dt <- trace_dt[, .(attack_prob = (sum(attack) + global_mean)/(.N + 1),
                           n_of_traces = .N), 
                       by = compacted_trace]

# here I'm relying on the assumption about the same ordering of variants - might be risky
variant_dt[, cluster_id := cutree(hclustering, k = 5)]


fig2 <- ggplot(data.table(mds_embedds), aes(x=V1, y=V2, 
                                            colour = variant_dt[, attack_prob], 
                                            #shape = factor(variant_dt[, cluster_id]),
                                            size = variant_dt[, log(n_of_traces)]
                                            )) +
  geom_point() + 
  scale_color_gradient2(midpoint=0.5, low="blue", mid="white",
                        high="red", space ="Lab" ) +
  labs(x = "latent dim 1", y = "latent dim 2", colour = "attack prob", size = "log(#traces)",
       title = "Multidimensional scaling of trace variants")
fig2

dist_array <- as.matrix(dist_matrix)

variant_dt[, distance_to_cluster_members := 
              {sapply(.I, function(index) {
                            id = variant_dt[index, cluster_id]; 
                            sum(dist_array[index, variant_dt[, which(cluster_id == id)]] * 
                                variant_dt[cluster_id == id, n_of_traces])
                          })
              }]

# selecting cluster representatives
n_of_reps <- 5
cluster_reps <- variant_dt[, {id = first(order(distance_to_cluster_members), n_of_reps); 
                              .(index = .I[id],
                                n_of_traces = n_of_traces[id], 
                                attack_prob = attack_prob[id],
                                distance_to_cluster_members = distance_to_cluster_members[id]
                              )},
                           by = cluster_id]

fig3 <- ggplot(data.table(mds_embedds), aes(x=V1, y=V2, 
                                            colour = variant_dt[, attack_prob], 
                                            #shape = factor(variant_dt[, cluster_id]),
                                            size = variant_dt[, log(n_of_traces)]
                                            )) +
  geom_point() + 
  scale_color_gradient2(midpoint=0.5, low="blue", mid="white",
                        high="red", space ="Lab" ) +
  geom_point(data = data.table(mds_embedds[cluster_reps[, index], ]),
             color = variant_dt[cluster_reps[, index], factor(cluster_id)], 
             size = 2) + 
  labs(x = "latent dim 1", y = "latent dim 2", colour = "attack prob", size = "log(#traces)",
       title = "Visualization of variants with marked cluster representatives")
fig3


# selecting the largest variants
n_of_largest <- 5
largest_variants <- variant_dt[, {id = last(order(n_of_traces), n_of_largest); 
                                .(index = .I[id],
                                  n_of_traces = n_of_traces[id], 
                                  attack_prob = attack_prob[id],
                                  distance_to_cluster_members = distance_to_cluster_members[id]
                                )},
                              by = cluster_id]

fig4 <- ggplot(data.table(mds_embedds), aes(x=V1, y=V2, 
                                            colour = variant_dt[, attack_prob], 
                                            size = variant_dt[, log(n_of_traces)]
                                            )) +
  geom_point() + 
  scale_color_gradient2(midpoint=0.5, low="blue", mid="white",
                        high="red", space ="Lab" ) +
  geom_point(data = data.table(mds_embedds[largest_variants[, index], ]),
             color = variant_dt[largest_variants[, index], factor(cluster_id)], 
             size = 2) + 
  labs(x = "latent dim 1", y = "latent dim 2", colour = "attack prob", size = "log(#traces)",
       title = "Visualization of variants with marked largest variants for each cluster")
fig4


# selecting the largest variants
n_of_extreme_attack_probs <- 5
min_trace_count <- 10
extreme_variants <- variant_dt[n_of_traces > min_trace_count, 
                               {id = order(attack_prob); 
                                id = c(first(id, n_of_extreme_attack_probs), 
                                       last(id, n_of_extreme_attack_probs)); 
                              .(index = .I[id],
                                n_of_traces = n_of_traces[id], 
                                attack_prob = attack_prob[id],
                                distance_to_cluster_members = distance_to_cluster_members[id]
                              )},
                           by = .(cluster_id)]

fig5 <- ggplot(data.table(mds_embedds), aes(x=V1, y=V2, 
                                            colour = variant_dt[, attack_prob], 
                                            size = variant_dt[, log(n_of_traces)]
                                            )) +
  geom_point() + 
  scale_color_gradient2(midpoint=0.5, low="blue", mid="white",
                        high="red", space ="Lab" ) +
  geom_point(data = data.table(mds_embedds[extreme_variants[, index], ]),
             colour = variant_dt[extreme_variants[, index], factor(cluster_id)], 
             size = 2) + 
  labs(x = "latent dim 1", y = "latent dim 2", colour = "attack prob", size = "log(#traces)",
       title = "Visualization of variants with extreme attack probabilities")
fig5


unique(rbind(cluster_reps, largest_variants, extreme_variants))
save(variant_dt, cluster_reps, largest_variants, extreme_variants,
     file = "selected_variants.RData")

   

LS0tDQp0aXRsZTogIkN5YmVyc2VjdXJpdHkgVGhyZWF0IERldGVjdGlvbiBpbiB0aGUgQmVoYXZpb3Igb2YgSW9UIERldmljZXMiDQphdXRob3I6ICJIaWRkZW4gZm9yIHRoZSB0aW1lIG9mIHRoZSBkb3VibGUtYmxpbmQgcmV2aWV3IHByb2Nlc3MiDQpkYXRlOiAiYHIgU3lzLkRhdGUoKWAiDQpvdXRwdXQ6DQogIGh0bWxfbm90ZWJvb2s6DQogICAgZGZfcHJpbnQ6IHBhZ2VkDQogICAgZmlnX2hlaWdodDogMTANCiAgICBmaWdfd2lkdGg6IDEwDQogICAgcm93cy5wcmludDogMTANCiAgICBjc3M6IGRvYy5jc3MNCiAgaHRtbF9kb2N1bWVudDoNCiAgICBkZl9wcmludDogcGFnZWQNCiAgICBmaWdfaGVpZ2h0OiAxMA0KICAgIGZpZ193aWR0aDogMTANCiAgICByb3dzLnByaW50OiAxMA0KICAgIGNzczogZG9jLmNzcw0KLS0tDQoNCiMjIyBUaGUgcGxhbjoNCg0KMS4gTG9hZGluZyBhbGwgZGF0YS4NCjIuIEV4dHJhY3Rpbmcgc3RhdGlzdGljcyByZWdhcmRpbmcgUElEcyAoY2FzZXMpIGFuZCBzeXNjYWxsIHR5cGVzIChhY3Rpb25zKS4NCjMuIENvbXB1dGF0aW9uIG9mIGJhc2ljIGFnZ3JlZ2F0aW9ucyBhbmQgdmlzdWFsaXphdGlvbiBvZiB0aGUgcmVzdWx0cy4gIA0KDQpUaGUgb3JpZ2luYWwgc291cmNlIHN5c3RlbSBhdWRpdCBsb2dzIGFyZSB0aGUgcHJvcGVydHkgb2YgS25vd2xlZGdlUGl0LmFpIHBsYXRmb3JtIGFuZCBjYW4gYmUgZG93bmxvYWRlZCBmcm9tOg0KaHR0cHM6Ly9rbm93bGVkZ2VwaXQuYWkvZmVkY3Npcy0yMDIzLWNoYWxsZW5nZS8NCg0KYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0UsIHJlc3VsdHM9RkFMU0V9DQpvcHRpb25zKHdpZHRoID0gMTIwKQ0KDQojIGluc3RhbGxhdGlvbiBvZiB0aGUgcmVxdWlyZWQgcGFja2FnZXMgKHVuY29tbWVudCB0aGUgbGluZXMgYmVsb3cpOg0KIyBpbnN0YWxsLnBhY2thZ2VzKGMoImRhdGEudGFibGUiLCAiYXJ1bGVzIiwgImFydWxlc1NlcXVlbmNlcyIsICJhcnVsZXNWaXoiLCANCiMgICAgICAgICAgICAgICAgICAgICJnZ3Bsb3QyIiwgImx1YnJpZGF0ZSIsICJmYXN0dGltZSIsICJNYXRyaXgiKSkNCg0KbGlicmFyeShkYXRhLnRhYmxlKQ0KbGlicmFyeShNYXRyaXgpDQpsaWJyYXJ5KGFydWxlcykNCmxpYnJhcnkoYXJ1bGVzU2VxdWVuY2VzKQ0KbGlicmFyeShhcnVsZXNWaXopDQpsaWJyYXJ5KGdncGxvdDIpDQpsaWJyYXJ5KGx1YnJpZGF0ZSkNCmxpYnJhcnkoZmFzdHRpbWUpDQoNCmxpYnJhcnkoYnVwYXZlcnNlKQ0KDQpuX2NvcmVzIDwtIDYNCg0KIyBsb2NhbCBkYXRhIHBhdGhzDQpkYXRhX2RpciA8LSAiZGF0YSINCmRhdGFfZGlyX3RyIDwtICJkYXRhL3RyYWluX2RhdGEiDQpkYXRhX2Rpcl90ZSA8LSAiZGF0YS90ZXN0X2RhdGEiDQoNCiMgbG9jYWwgZmlsZSBuYW1lcw0KdHJhaW5pbmdfYXR0YWNrc19maWxlIDwtICJ0cmFpbl9maWxlc19jb250YWluaW5nX2F0dGFja3MudHh0Ig0KdGVzdF9hdHRhY2tzX2ZpbGUgPC0gInRlc3RfZmlsZXNfY29udGFpbmluZ19hdHRhY2tzLnR4dCINCg0KIyBnZXR0aW5nIGxpc3RzIG9mIGZpbGVzIHdpdGggYXVkaXQgbG9ncw0KdHJhaW5pbmdfZmlsZV9saXN0IDwtIGRpcihmaWxlLnBhdGgoZ2V0d2QoKSwgZGF0YV9kaXJfdHIpKQ0KdGVzdF9maWxlX2xpc3QgPC0gZGlyKGZpbGUucGF0aChnZXR3ZCgpLCBkYXRhX2Rpcl90ZSkpDQphdHRhY2tfZmlsZV9uYW1lcyA8LSBjKHJlYWRMaW5lcyhmaWxlLnBhdGgoZGF0YV9kaXIsIHRyYWluaW5nX2F0dGFja3NfZmlsZSkpLA0KICAgICAgICAgICAgICAgICAgICAgICByZWFkTGluZXMoZmlsZS5wYXRoKGRhdGFfZGlyLCB0ZXN0X2F0dGFja3NfZmlsZSkpKQ0KDQojIGN1c3RvbSBmdW5jdGlvbiBkZWZpbml0aW9ucw0KZXh0cmFjdF9iYXNpY19pbmZvIDwtIGZ1bmN0aW9uKGxvZ3MpIHsNCiAgbl9vZl9waWRzIDwtIGxvZ3NbLCB1bmlxdWVOKFNZU0NBTExfcGlkKV0NCiAgbl9vZl91bmlxdWVfYWN0aW9ucyA8LSBsb2dzWywgdW5pcXVlTihTWVNDQUxMX3N5c2NhbGwpXQ0KICBuX29mX2V2ZW50cyA8LSBucm93KGxvZ3MpDQogIGF2Z19hY3Rpb25zX3Blcl9waWQgPC0gbl9vZl9ldmVudHMvbl9vZl9waWRzDQogIGFjdGlvbnNfcGVyX3BpZF9xdWFudHMgPC0gbG9nc1ssIC5OLCBTWVNDQUxMX3BpZF1bLCBxdWFudGlsZShOLCBjKDAsIDAuMSwgMC4yNSwgMC41LCAwLjc1LCAwLjksIDEuMCkpXQ0KICANCiAgbGlzdChuX29mX3BpZHMgPSBuX29mX3BpZHMsDQogICAgICAgbl9vZl91bmlxdWVfYWN0aW9ucyA9IG5fb2ZfdW5pcXVlX2FjdGlvbnMsDQogICAgICAgbl9vZl9ldmVudHMgPSBuX29mX2V2ZW50cywNCiAgICAgICBhdmdfYWN0aW9uc19wZXJfcGlkID0gYXZnX2FjdGlvbnNfcGVyX3BpZCwNCiAgICAgICBhY3Rpb25zX3Blcl9waWRfcXVhbnRzID0gYWN0aW9uc19wZXJfcGlkX3F1YW50cw0KICApDQp9DQoNCmdldFNlcXVlbmNlSUQgPC0gZnVuY3Rpb24oaXRlbXNldElEcykgew0KICBzYXBwbHkoc3Ryc3BsaXQoaXRlbXNldElEcywgIl8iKSwgZnVuY3Rpb24oeCkgcGFzdGUwKHhbMV0sICJfIiwgeFsyXSkpDQp9DQoNCmdldFRpbWVzdGFtcCA8LSBmdW5jdGlvbihpdGVtc2V0SURzKSB7DQogIHNhcHBseShzdHJzcGxpdChpdGVtc2V0SURzLCAiXyIpLCBmdW5jdGlvbih4KSBhcy5pbnRlZ2VyKHhbM10pKQ0KfQ0KDQojIGFuIGF1eGlsaWFyeSBmdW5jdGlvbiBmb3IgY29tcGFjdGluZyB0cmFjZXMNCmNvbXBhY3RfdHJhY2VzIDwtIGZ1bmN0aW9uKHRyYWNlX2R0LCB1bmlxdWVfYWN0aW9uX2lkcywgc2hvd19wcm9ncmVzcyA9IFRSVUUpIHsNCiAgdHJhY2VfZHRbLCBjb21wYWN0ZWRfdHJhY2UgOj0gY29weSh0cmFjZSldDQogIA0KICB0cmFjZV9kdFssIGNvbXBhY3RlZF90cmFjZSA6PSBnc3ViKCJyb290X2Nsb3NlX3llcyxyb290X29wZW5hdF95ZXMiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJyb290X2Nsb3NlX29wZW5hdF95ZXMiLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb21wYWN0ZWRfdHJhY2UpXQ0KICB0cmFjZV9kdFssIGNvbXBhY3RlZF90cmFjZSA6PSBnc3ViKCIoLHJvb3RfY2xvc2Vfb3BlbmF0X3llcyl7Mix9IiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIixtdWx0aV9yb290X2Nsb3NlX29wZW5hdF95ZXMiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbXBhY3RlZF90cmFjZSldDQogIHRyYWNlX2R0WywgY29tcGFjdGVkX3RyYWNlIDo9IGdzdWIoIl5yb290X2Nsb3NlX29wZW5hdF95ZXMsbXVsdGlfcm9vdF9jbG9zZV9vcGVuYXRfeWVzIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibXVsdGlfcm9vdF9jbG9zZV9vcGVuYXRfeWVzIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb21wYWN0ZWRfdHJhY2UpXQ0KICB0cmFjZV9kdFssIGNvbXBhY3RlZF90cmFjZSA6PSBnc3ViKCJecm9vdF9jbG9zZV9vcGVuYXRfeWVzLHJvb3RfY2xvc2Vfb3BlbmF0X3llcyIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm11bHRpX3Jvb3RfY2xvc2Vfb3BlbmF0X3llcyIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29tcGFjdGVkX3RyYWNlKV0NCiAgDQogIGZvcihhY3Rpb24gaW4gdW5pcXVlX2FjdGlvbl9pZHMpIHsNCiAgICB0cmFjZV9kdFssIGNvbXBhY3RlZF90cmFjZSA6PSBnc3ViKHBhc3RlMCgiKCwiLCBhY3Rpb24sICIpezIsfSIpLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBhc3RlMCgiLG11bHRpXyIsIGFjdGlvbiksIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29tcGFjdGVkX3RyYWNlKV0NCiAgICB0cmFjZV9kdFssIGNvbXBhY3RlZF90cmFjZSA6PSBnc3ViKHBhc3RlMCgiXiIsIGFjdGlvbiwgIixtdWx0aV8iLCBhY3Rpb24pLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBhc3RlMCgibXVsdGlfIiwgYWN0aW9uKSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb21wYWN0ZWRfdHJhY2UpXQ0KICAgIHRyYWNlX2R0WywgY29tcGFjdGVkX3RyYWNlIDo9IGdzdWIocGFzdGUwKCJeIiwgYWN0aW9uLCAiLCIsIGFjdGlvbiksIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcGFzdGUwKCJtdWx0aV8iLCBhY3Rpb24pLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbXBhY3RlZF90cmFjZSldDQogICAgaWYoc2hvd19wcm9ncmVzcykgcHJpbnQoYWN0aW9uKQ0KICB9DQogIHRyYWNlX2R0WywgdW5pcXVlTihjb21wYWN0ZWRfdHJhY2UpXQ0KICB0cmFjZV9kdFssIGNvbXBhY3RlZF90cmFjZV9sZW5ndGggOj0gc2FwcGx5KHN0cnNwbGl0KGNvbXBhY3RlZF90cmFjZSwgIiwiKSwgbGVuZ3RoKV0NCiAgdHJhY2VfZHQNCn0NCmBgYA0KDQpUaGUgYXZhaWxhYmxlIGRhdGEgY29udGFpbnMgYHIgbGVuZ3RoKHRyYWluaW5nX2ZpbGVfbGlzdCkgKyBsZW5ndGgodGVzdF9maWxlX2xpc3QpYCBsb2cgZmlsZXMuIEVhY2ggb2YgdGhlIGZpbGVzIGNvcnJlc3BvbmRzIHRvIDEwIG1pbnV0ZXMgb2Ygc3lzY2FsbCBoaXN0b3J5LiBJbiB0b3RhbCwgYHIgbGVuZ3RoKGF0dGFja19maWxlX25hbWVzKWAgbG9nIGZpbGVzIChgciByb3VuZCgxMDAqbGVuZ3RoKGF0dGFja19maWxlX25hbWVzKS8obGVuZ3RoKHRyYWluaW5nX2ZpbGVfbGlzdCkgKyBsZW5ndGgodGVzdF9maWxlX2xpc3QpKSwgMilgXCUpIGNvcnJlc3BvbmQgdG8gYXR0YWNrcyBvbiB0aGUgZGV2aWNlcy4NCg0KVG8gZW5hYmxlIHRoZSBhbmFseXNpcyBvZiB0aGlzIGRhdGEgdXNpbmcgcHJvY2VzcyBtaW5pbmcgdGVjaG5pcXVlcywgSSB3b3VsZCBzdWdnZXN0IHRvIGNvbnNpZGVyIHRoZSBjb21iaW5hdGlvbiBvZiBhIHRpbWUgd2luZG93IElEICggX190aW1lX3dpbmRvd19pZF9fICkgYW5kIF9fU1lTQ0FMTF9waWRfXyB2YWx1ZXMgYXMgX2Nhc2VzXyBhbmQgY29tYmluYXRpb25zIG9mIF9fU1lTQ0FMTF9zeXNjYWxsX18sIF9fU1lTQ0FMTF9zdWNjZXNzX18sIGFuZCBtYXliZSBfX1BST0NFU1NfdWlkX18gdmFsdWVzIGFzIF9hY3Rpb25zXy4gDQoNCkkgdHJhbnNmb3JtIHRoZSBkYXRhIGJ5IHNlbGVjdGluZyBfX1NZU0NBTExfdGltZXN0YW1wX18sIF9fU1lTQ0FMTF9waWRfXywgX19TWVNDQUxMX3N1Y2Nlc3NfXywgX19QUk9DRVNTX3VpZF9fLCBhbmQgX19TWVNDQUxMX3N5c2NhbGxfXyBjb2x1bW5zLiBJIGFsc28gbWVyZ2UgYWxsIGRhdGEgdGFibGVzIGludG8gYSBzaW5nbGUgdGFibGUgZm9yIG1vcmUgY29udmVuaWVudCBwcm9jZXNzaW5nLiBGaW5hbGx5LCBJIGRpdmlkZSB0aGUgZGF0YSBpbnRvIHRocmVlIHBhcnRzOg0KICANCiAgLSBfcHJvY2Vzc19kaXNjb3ZlcnlfIHBhcnQsICAgDQogIC0gX21vZGVsX3RyYWluaW5nXyBwYXJ0LA0KICAtIF90ZXN0X2RhdGFfIHBhcnQuDQoNCmBgYHtyIHByb2Nlc3NlZF9kYXRhX2xvYWRpbmcsIHJlc3VsdHM9J2hpZGUnfQ0KbG9hZChmaWxlID0gInByb2Nlc3NfZGF0YV92Mi5SRGF0YSIpDQoNCnByb2Nlc3NfZGlzY292ZXJ5X2RhdGFbU1lTQ0FMTF9zdWNjZXNzID09ICIiLCBTWVNDQUxMX3N1Y2Nlc3MgOj0gIk5BIl0NCm1vZGVsX3RyYWluaW5nX2RhdGFbU1lTQ0FMTF9zdWNjZXNzID09ICIiLCBTWVNDQUxMX3N1Y2Nlc3MgOj0gIk5BIl0NCnRlc3RfZGF0YVtTWVNDQUxMX3N1Y2Nlc3MgPT0gIiIsIFNZU0NBTExfc3VjY2VzcyA6PSAiTkEiXQ0KDQpzZXRrZXkocHJvY2Vzc19kaXNjb3ZlcnlfZGF0YSwgdGltZV93aW5kb3dfaWQsIFNZU0NBTExfdGltZXN0YW1wKQ0Kc2V0a2V5KG1vZGVsX3RyYWluaW5nX2RhdGEsIHRpbWVfd2luZG93X2lkLCBTWVNDQUxMX3RpbWVzdGFtcCkNCnNldGtleSh0ZXN0X2RhdGEsIHRpbWVfd2luZG93X2lkLCBTWVNDQUxMX3RpbWVzdGFtcCkNCmBgYA0KDQpgYGB7ciBidXBhcl9leHBlcmltLCB3YXJuaW5nPUZBTFNFLCBmaWcuaG9sZD0naG9sZCcsIG91dC53aWR0aD0iMTAwJSIsIGZpZy53aWR0aD0xMn0NCmV2ZW50bG9nX2RhdGEgPC0gZXZlbnRsb2coYXMuZGF0YS5mcmFtZShwcm9jZXNzX2Rpc2NvdmVyeV9kYXRhKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgY2FzZV9pZCA9IGMoInRpbWVfd2luZG93X2lkIiwgIlNZU0NBTExfcGlkIiksDQogICAgICAgICAgICAgICAgICAgICAgICAgIHRpbWVzdGFtcCA9ICJTWVNDQUxMX3RpbWVzdGFtcCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgIGFjdGl2aXR5X2lkID0gYygiUFJPQ0VTU191aWQiLCAiU1lTQ0FMTF9zeXNjYWxsIiwgIlNZU0NBTExfc3VjY2VzcyIpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICBhY3Rpdml0eV9pbnN0YW5jZV9pZCA9IGMoInRpbWVfd2luZG93X2lkIiwgIlNZU0NBTExfcGlkIiwgIlBST0NFU1NfdWlkIiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiU1lTQ0FMTF9zeXNjYWxsIiwgIlNZU0NBTExfc3VjY2VzcyIsICJTWVNDQUxMX3RpbWVzdGFtcCIpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICByZXNvdXJjZV9pZCA9IGMoIlBST0NFU1NfdWlkIiksDQogICAgICAgICAgICAgICAgICAgICAgICAgIGxpZmVjeWNsZV9pZCA9ICJpc19hdHRhY2siLA0KICAgICAgICAgICAgICAgICAgICAgICAgICBvcmRlciA9ICJhbHBoYWJldGljYWwiDQogICAgICAgICAgICAgICAgICAgICAgICAgICkNCg0KZXZlbnRsb2dfZGF0YQ0KDQp0cmFjZV9kYXRhIDwtIHRyYWNlcyhldmVudGxvZ19kYXRhKQ0KdHJhY2VfZGF0YQ0KDQpmcmVxdWVudF90cmFjZXMgPC0gZmlsdGVyX3RyYWNlX2ZyZXF1ZW5jeShldmVudGxvZ19kYXRhLCBwZXJjZW50YWdlID0gLjgpDQpmcmVxdWVudF90cmFjZXMNCg0KYXR0YWNrX3RyYWNlcyA8LSBmaWx0ZXJfbGlmZWN5Y2xlX3ByZXNlbmNlKGV2ZW50bG9nX2RhdGEsIGxpZmVjeWNsZXMgPSAiVFJVRSIsIG1ldGhvZCA9ICJhbGwiKQ0KYXR0YWNrX3RyYWNlcw0KYGBgDQpgYGB7ciBwbG90aW5nX3ZhcmlhbnRzXzEsIHdhcm5pbmc9RkFMU0UsIGZpZy5ob2xkPSdob2xkJywgb3V0LndpZHRoPSIxMDAlIiwgZmlnLndpZHRoPTE4fQ0KZmxvd19wbG90IDwtIHByb2Nlc3NfbWFwKGZyZXF1ZW50X3RyYWNlcywgdHlwZSA9IGZyZXF1ZW5jeSgicmVsYXRpdmUiKSkNCmZsb3dfcGxvdA0KDQpmcmVxdWVudF9hdHRhY2tfdHJhY2VzIDwtIGZpbHRlcl90cmFjZV9mcmVxdWVuY3koYXR0YWNrX3RyYWNlcywgcGVyY2VudGFnZSA9IC44KQ0KYXR0YWNrc19mbG93X3Bsb3QgPC0gcHJvY2Vzc19tYXAoZnJlcXVlbnRfYXR0YWNrX3RyYWNlcywgdHlwZSA9IGZyZXF1ZW5jeSgicmVsYXRpdmUiKSkNCmF0dGFja3NfZmxvd19wbG90DQoNCnZhcmlhbnRfdml6IDwtIHRyYWNlX2V4cGxvcmVyKGV2ZW50bG9nX2RhdGEsIGNvdmVyYWdlID0gMC41KQ0KdmFyaWFudF92aXoNCmBgYA0KDQpgYGB7ciBjb21wYWN0aW5nX3RyYWNlc30NCmV2ZW50bG9nX2RhdGFfZHQgPC0gYXMuZGF0YS50YWJsZShldmVudGxvZ19kYXRhKQ0KDQp1bmlxdWVfYWN0aW9uX2lkcyA8LSBldmVudGxvZ19kYXRhX2R0WywgdW5pcXVlKFBST0NFU1NfdWlkX1NZU0NBTExfc3lzY2FsbF9TWVNDQUxMX3N1Y2Nlc3MpXQ0KdHJhY2VfZHQgPC0gZXZlbnRsb2dfZGF0YV9kdFssIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAuKHRyYWNlID0gcGFzdGUoUFJPQ0VTU191aWRfU1lTQ0FMTF9zeXNjYWxsX1NZU0NBTExfc3VjY2VzcywgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xsYXBzZSA9ICIsIiksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdHJhY2VfbGVuZ3RoID0gLk4pLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICBieSA9IHRpbWVfd2luZG93X2lkX1NZU0NBTExfcGlkXQ0KDQpkaW0odHJhY2VfZHQpDQp0cmFjZV9kdFssIHVuaXF1ZU4odHJhY2UpXQ0KDQp0cmFjZV9kdCA8LSBjb21wYWN0X3RyYWNlcyh0cmFjZV9kdCwgdW5pcXVlX2FjdGlvbl9pZHMsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgc2hvd19wcm9ncmVzcyA9IEZBTFNFKQ0KYGBgDQoNCmBgYHtyIGNsdXN0ZXJpbmdfdHJhY2VzLCBmaWcuaG9sZD0naG9sZCcsIG91dC53aWR0aD0iMTAwJSIsIGZpZy53aWR0aD03LCBmaWcuaGVpZ2h0PTZ9DQpub3JtYWxpemVkX2xldmVuc2h0ZWluX2Rpc3QgPC0gZnVuY3Rpb24oeCwgeSkgew0KICBsZGlzdCA8LSBwcm94eTo6ZGlzdChsaXN0KHgpLCBsaXN0KHkpLCBtZXRob2QgPSAiTGV2ZW5zaHRlaW4iKQ0KICBhcy5udW1lcmljKGxkaXN0KS8obGVuZ3RoKHgpICsgbGVuZ3RoKHkpKQ0KfQ0KDQpkaXN0X21hdHJpeCA8LSBwcm94eTo6ZGlzdChzdHJzcGxpdCh0cmFjZV9kdFssIHVuaXF1ZShjb21wYWN0ZWRfdHJhY2UpXSwgIiwiKSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICBtZXRob2QgPSBub3JtYWxpemVkX2xldmVuc2h0ZWluX2Rpc3QpDQoNCmhjbHVzdGVyaW5nIDwtIGhjbHVzdChkaXN0X21hdHJpeCwgbWV0aG9kID0gIndhcmQuRCIpDQoNCm1kc19lbWJlZGRzIDwtIGNtZHNjYWxlKGRpc3RfbWF0cml4LCBrID0gMikNCnBsb3QobWRzX2VtYmVkZHMsIGNvbCA9IGN1dHJlZShoY2x1c3RlcmluZywgayA9IDUpLCANCiAgICAgeGxhYiA9ICJsYXRlbnQgZGltIDEiLCANCiAgICAgeWxhYiA9ICJsYXRlbnQgZGltIDIiLA0KICAgICBtYWluID0gIk11bHRpZGltZW5zaW9uYWwgc2NhbGluZyBvZiB0cmFjZSB2YXJpYW50cyIpDQpzYXZlKGRpc3RfbWF0cml4LCBtZHNfZW1iZWRkcywgZmlsZSA9ICJ2YXJpYW50X2Rpc3RhbmNlX21hdHJpeC5SRGF0YSIpDQoNCmZpZzEgPC0gZ2dwbG90KGRhdGEudGFibGUobWRzX2VtYmVkZHMpLCANCiAgICAgICAgICAgICAgIGFlcyh4PVYxLCB5PVYyLCBjb2xvdXIgPSBmYWN0b3IoY3V0cmVlKGhjbHVzdGVyaW5nLCBrID0gNSkpKSkgKw0KICBnZW9tX3BvaW50KCkgKyANCiAgbGFicyh4ID0gImxhdGVudCBkaW0gMSIsIHkgPSAibGF0ZW50IGRpbSAyIiwgY29sb3VyID0gImdyb3VwIiwgDQogICAgICAgdGl0bGUgPSAiTXVsdGlkaW1lbnNpb25hbCBzY2FsaW5nIG9mIHRyYWNlIHZhcmlhbnRzIikNCmZpZzENCmBgYA0KDQpgYGB7ciBhZGRpbmdfYXR0YWNrcywgZmlnLmhvbGQ9J2hvbGQnLCBvdXQud2lkdGg9IjEwMCUiLCBmaWcud2lkdGg9NywgZmlnLmhlaWdodD02fQ0KcHJvY2Vzc19kaXNjb3ZlcnlfZGF0YVssIHRpbWVfd2luZG93X2lkX1NZU0NBTExfcGlkIDo9IHBhc3RlMCh0aW1lX3dpbmRvd19pZCwgIl8iLCBTWVNDQUxMX3BpZCldDQp0cmFjZV9kdCA8LSB0cmFjZV9kdFtwcm9jZXNzX2Rpc2NvdmVyeV9kYXRhWywgLihhdHRhY2sgPSBpc19hdHRhY2tbMV0pLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYnkgPSB0aW1lX3dpbmRvd19pZF9TWVNDQUxMX3BpZF0sIA0KICAgICAgICAgICAgICAgICAgICAgb24gPSAidGltZV93aW5kb3dfaWRfU1lTQ0FMTF9waWQiXQ0KDQpnbG9iYWxfbWVhbiA8LSB0cmFjZV9kdFssIG1lYW4oYXR0YWNrKV0NCnZhcmlhbnRfZHQgPC0gdHJhY2VfZHRbLCAuKGF0dGFja19wcm9iID0gKHN1bShhdHRhY2spICsgZ2xvYmFsX21lYW4pLyguTiArIDEpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgbl9vZl90cmFjZXMgPSAuTiksIA0KICAgICAgICAgICAgICAgICAgICAgICBieSA9IGNvbXBhY3RlZF90cmFjZV0NCg0KIyBoZXJlIEknbSByZWx5aW5nIG9uIHRoZSBhc3N1bXB0aW9uIGFib3V0IHRoZSBzYW1lIG9yZGVyaW5nIG9mIHZhcmlhbnRzIC0gbWlnaHQgYmUgcmlza3kNCnZhcmlhbnRfZHRbLCBjbHVzdGVyX2lkIDo9IGN1dHJlZShoY2x1c3RlcmluZywgayA9IDUpXQ0KDQoNCmZpZzIgPC0gZ2dwbG90KGRhdGEudGFibGUobWRzX2VtYmVkZHMpLCBhZXMoeD1WMSwgeT1WMiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG91ciA9IHZhcmlhbnRfZHRbLCBhdHRhY2tfcHJvYl0sIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjc2hhcGUgPSBmYWN0b3IodmFyaWFudF9kdFssIGNsdXN0ZXJfaWRdKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2l6ZSA9IHZhcmlhbnRfZHRbLCBsb2cobl9vZl90cmFjZXMpXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApKSArDQogIGdlb21fcG9pbnQoKSArIA0KICBzY2FsZV9jb2xvcl9ncmFkaWVudDIobWlkcG9pbnQ9MC41LCBsb3c9ImJsdWUiLCBtaWQ9IndoaXRlIiwNCiAgICAgICAgICAgICAgICAgICAgICAgIGhpZ2g9InJlZCIsIHNwYWNlID0iTGFiIiApICsNCiAgbGFicyh4ID0gImxhdGVudCBkaW0gMSIsIHkgPSAibGF0ZW50IGRpbSAyIiwgY29sb3VyID0gImF0dGFjayBwcm9iIiwgc2l6ZSA9ICJsb2coI3RyYWNlcykiLA0KICAgICAgIHRpdGxlID0gIk11bHRpZGltZW5zaW9uYWwgc2NhbGluZyBvZiB0cmFjZSB2YXJpYW50cyIpDQpmaWcyDQpgYGANCg0KDQpgYGB7ciBzZWxlY3RpbmdfY2x1c3Rlcl9yZXBzLCBmaWcuaG9sZD0naG9sZCcsIG91dC53aWR0aD0iMTAwJSIsIGZpZy53aWR0aD03LCBmaWcuaGVpZ2h0PTZ9DQpkaXN0X2FycmF5IDwtIGFzLm1hdHJpeChkaXN0X21hdHJpeCkNCg0KdmFyaWFudF9kdFssIGRpc3RhbmNlX3RvX2NsdXN0ZXJfbWVtYmVycyA6PSANCiAgICAgICAgICAgICAge3NhcHBseSguSSwgZnVuY3Rpb24oaW5kZXgpIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZCA9IHZhcmlhbnRfZHRbaW5kZXgsIGNsdXN0ZXJfaWRdOyANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdW0oZGlzdF9hcnJheVtpbmRleCwgdmFyaWFudF9kdFssIHdoaWNoKGNsdXN0ZXJfaWQgPT0gaWQpXV0gKiANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdmFyaWFudF9kdFtjbHVzdGVyX2lkID09IGlkLCBuX29mX3RyYWNlc10pDQogICAgICAgICAgICAgICAgICAgICAgICAgIH0pDQogICAgICAgICAgICAgIH1dDQoNCiMgc2VsZWN0aW5nIGNsdXN0ZXIgcmVwcmVzZW50YXRpdmVzDQpuX29mX3JlcHMgPC0gNQ0KY2x1c3Rlcl9yZXBzIDwtIHZhcmlhbnRfZHRbLCB7aWQgPSBmaXJzdChvcmRlcihkaXN0YW5jZV90b19jbHVzdGVyX21lbWJlcnMpLCBuX29mX3JlcHMpOyANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC4oaW5kZXggPSAuSVtpZF0sDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5fb2ZfdHJhY2VzID0gbl9vZl90cmFjZXNbaWRdLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYXR0YWNrX3Byb2IgPSBhdHRhY2tfcHJvYltpZF0sDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRpc3RhbmNlX3RvX2NsdXN0ZXJfbWVtYmVycyA9IGRpc3RhbmNlX3RvX2NsdXN0ZXJfbWVtYmVyc1tpZF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICl9LA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgYnkgPSBjbHVzdGVyX2lkXQ0KDQpmaWczIDwtIGdncGxvdChkYXRhLnRhYmxlKG1kc19lbWJlZGRzKSwgYWVzKHg9VjEsIHk9VjIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xvdXIgPSB2YXJpYW50X2R0WywgYXR0YWNrX3Byb2JdLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgI3NoYXBlID0gZmFjdG9yKHZhcmlhbnRfZHRbLCBjbHVzdGVyX2lkXSksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNpemUgPSB2YXJpYW50X2R0WywgbG9nKG5fb2ZfdHJhY2VzKV0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSkgKw0KICBnZW9tX3BvaW50KCkgKyANCiAgc2NhbGVfY29sb3JfZ3JhZGllbnQyKG1pZHBvaW50PTAuNSwgbG93PSJibHVlIiwgbWlkPSJ3aGl0ZSIsDQogICAgICAgICAgICAgICAgICAgICAgICBoaWdoPSJyZWQiLCBzcGFjZSA9IkxhYiIgKSArDQogIGdlb21fcG9pbnQoZGF0YSA9IGRhdGEudGFibGUobWRzX2VtYmVkZHNbY2x1c3Rlcl9yZXBzWywgaW5kZXhdLCBdKSwNCiAgICAgICAgICAgICBjb2xvciA9IHZhcmlhbnRfZHRbY2x1c3Rlcl9yZXBzWywgaW5kZXhdLCBmYWN0b3IoY2x1c3Rlcl9pZCldLCANCiAgICAgICAgICAgICBzaXplID0gMikgKyANCiAgbGFicyh4ID0gImxhdGVudCBkaW0gMSIsIHkgPSAibGF0ZW50IGRpbSAyIiwgY29sb3VyID0gImF0dGFjayBwcm9iIiwgc2l6ZSA9ICJsb2coI3RyYWNlcykiLA0KICAgICAgIHRpdGxlID0gIlZpc3VhbGl6YXRpb24gb2YgdmFyaWFudHMgd2l0aCBtYXJrZWQgY2x1c3RlciByZXByZXNlbnRhdGl2ZXMiKQ0KZmlnMw0KDQojIHNlbGVjdGluZyB0aGUgbGFyZ2VzdCB2YXJpYW50cw0Kbl9vZl9sYXJnZXN0IDwtIDUNCmxhcmdlc3RfdmFyaWFudHMgPC0gdmFyaWFudF9kdFssIHtpZCA9IGxhc3Qob3JkZXIobl9vZl90cmFjZXMpLCBuX29mX2xhcmdlc3QpOyANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLihpbmRleCA9IC5JW2lkXSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuX29mX3RyYWNlcyA9IG5fb2ZfdHJhY2VzW2lkXSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYXR0YWNrX3Byb2IgPSBhdHRhY2tfcHJvYltpZF0sDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGlzdGFuY2VfdG9fY2x1c3Rlcl9tZW1iZXJzID0gZGlzdGFuY2VfdG9fY2x1c3Rlcl9tZW1iZXJzW2lkXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApfSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJ5ID0gY2x1c3Rlcl9pZF0NCg0KZmlnNCA8LSBnZ3Bsb3QoZGF0YS50YWJsZShtZHNfZW1iZWRkcyksIGFlcyh4PVYxLCB5PVYyLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sb3VyID0gdmFyaWFudF9kdFssIGF0dGFja19wcm9iXSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNpemUgPSB2YXJpYW50X2R0WywgbG9nKG5fb2ZfdHJhY2VzKV0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSkgKw0KICBnZW9tX3BvaW50KCkgKyANCiAgc2NhbGVfY29sb3JfZ3JhZGllbnQyKG1pZHBvaW50PTAuNSwgbG93PSJibHVlIiwgbWlkPSJ3aGl0ZSIsDQogICAgICAgICAgICAgICAgICAgICAgICBoaWdoPSJyZWQiLCBzcGFjZSA9IkxhYiIgKSArDQogIGdlb21fcG9pbnQoZGF0YSA9IGRhdGEudGFibGUobWRzX2VtYmVkZHNbbGFyZ2VzdF92YXJpYW50c1ssIGluZGV4XSwgXSksDQogICAgICAgICAgICAgY29sb3IgPSB2YXJpYW50X2R0W2xhcmdlc3RfdmFyaWFudHNbLCBpbmRleF0sIGZhY3RvcihjbHVzdGVyX2lkKV0sIA0KICAgICAgICAgICAgIHNpemUgPSAyKSArIA0KICBsYWJzKHggPSAibGF0ZW50IGRpbSAxIiwgeSA9ICJsYXRlbnQgZGltIDIiLCBjb2xvdXIgPSAiYXR0YWNrIHByb2IiLCBzaXplID0gImxvZygjdHJhY2VzKSIsDQogICAgICAgdGl0bGUgPSAiVmlzdWFsaXphdGlvbiBvZiB2YXJpYW50cyB3aXRoIG1hcmtlZCBsYXJnZXN0IHZhcmlhbnRzIGZvciBlYWNoIGNsdXN0ZXIiKQ0KZmlnNA0KDQojIHNlbGVjdGluZyB0aGUgbGFyZ2VzdCB2YXJpYW50cw0Kbl9vZl9leHRyZW1lX2F0dGFja19wcm9icyA8LSA1DQptaW5fdHJhY2VfY291bnQgPC0gMTANCmV4dHJlbWVfdmFyaWFudHMgPC0gdmFyaWFudF9kdFtuX29mX3RyYWNlcyA+IG1pbl90cmFjZV9jb3VudCwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAge2lkID0gb3JkZXIoYXR0YWNrX3Byb2IpOyANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWQgPSBjKGZpcnN0KGlkLCBuX29mX2V4dHJlbWVfYXR0YWNrX3Byb2JzKSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsYXN0KGlkLCBuX29mX2V4dHJlbWVfYXR0YWNrX3Byb2JzKSk7IA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLihpbmRleCA9IC5JW2lkXSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbl9vZl90cmFjZXMgPSBuX29mX3RyYWNlc1tpZF0sIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhdHRhY2tfcHJvYiA9IGF0dGFja19wcm9iW2lkXSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGlzdGFuY2VfdG9fY2x1c3Rlcl9tZW1iZXJzID0gZGlzdGFuY2VfdG9fY2x1c3Rlcl9tZW1iZXJzW2lkXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKX0sDQogICAgICAgICAgICAgICAgICAgICAgICAgICBieSA9IC4oY2x1c3Rlcl9pZCldDQoNCmZpZzUgPC0gZ2dwbG90KGRhdGEudGFibGUobWRzX2VtYmVkZHMpLCBhZXMoeD1WMSwgeT1WMiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG91ciA9IHZhcmlhbnRfZHRbLCBhdHRhY2tfcHJvYl0sIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzaXplID0gdmFyaWFudF9kdFssIGxvZyhuX29mX3RyYWNlcyldDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkpICsNCiAgZ2VvbV9wb2ludCgpICsgDQogIHNjYWxlX2NvbG9yX2dyYWRpZW50MihtaWRwb2ludD0wLjUsIGxvdz0iYmx1ZSIsIG1pZD0id2hpdGUiLA0KICAgICAgICAgICAgICAgICAgICAgICAgaGlnaD0icmVkIiwgc3BhY2UgPSJMYWIiICkgKw0KICBnZW9tX3BvaW50KGRhdGEgPSBkYXRhLnRhYmxlKG1kc19lbWJlZGRzW2V4dHJlbWVfdmFyaWFudHNbLCBpbmRleF0sIF0pLA0KICAgICAgICAgICAgIGNvbG91ciA9IHZhcmlhbnRfZHRbZXh0cmVtZV92YXJpYW50c1ssIGluZGV4XSwgZmFjdG9yKGNsdXN0ZXJfaWQpXSwgDQogICAgICAgICAgICAgc2l6ZSA9IDIpICsgDQogIGxhYnMoeCA9ICJsYXRlbnQgZGltIDEiLCB5ID0gImxhdGVudCBkaW0gMiIsIGNvbG91ciA9ICJhdHRhY2sgcHJvYiIsIHNpemUgPSAibG9nKCN0cmFjZXMpIiwNCiAgICAgICB0aXRsZSA9ICJWaXN1YWxpemF0aW9uIG9mIHZhcmlhbnRzIHdpdGggZXh0cmVtZSBhdHRhY2sgcHJvYmFiaWxpdGllcyIpDQpmaWc1DQoNCnVuaXF1ZShyYmluZChjbHVzdGVyX3JlcHMsIGxhcmdlc3RfdmFyaWFudHMsIGV4dHJlbWVfdmFyaWFudHMpKQ0Kc2F2ZSh2YXJpYW50X2R0LCBjbHVzdGVyX3JlcHMsIGxhcmdlc3RfdmFyaWFudHMsIGV4dHJlbWVfdmFyaWFudHMsDQogICAgIGZpbGUgPSAic2VsZWN0ZWRfdmFyaWFudHMuUkRhdGEiKQ0KYGBgDQoNCg0KDQoNClwgIA0KXCAgDQoNCg0K